#import sst
import os,re
try:
    import ConfigParser
except ImportError:     # Python 3.x
    import configparser as ConfigParser
    from functools import reduce
#from sst import merlin
import json


def connect(name, c0, port0, c1, port1, latency):
    link = sst.Link(name)
    link.connect( (c0, port0, latency), (c1, port1, latency) )
    return link


class JsonParser:
    def __init__(self, cf):
        self.json_file = cf

    def parse(self):
        group_base = dict()
        net_base = dict()
        fr = open(self.json_file, 'r')
        info = json.load(fr)
        for key, value in info.items():
            if(key != 'simulation'):
                continue
            sim_dict = value
        for key, value in sim_dict.items():
            if(key == 'Components'):
                comp_base = value
            elif(key == 'chiplets'):
                for k, v in value.items():
                    if(k == 'GroupNum' or k == 'Interconnect'):
                        net_base.update(v)
                group_base.update(self.group_basecfg(value, comp_base))

        print(group_base)
        for key, value in group_base.items():
            #print("group_base key %s \n" % key)
            if(key == 'GROUP0'):
                net_base.update(value['ROUTER'])
        print(net_base)
        return group_base, net_base

    #base configuration of group according to json
    def group_basecfg(self, group, comp):
        for key, value in group.items():
            if(key == 'GroupNum' or key == 'Interconnect'):
                continue
            if('CPU_type' not in value.keys()):
                print("*****Error: group %s has not specified CPU type"%key)
                sys.exit()
            cpu_type = value['CPU_type']
            value['CPU'] = comp[cpu_type]

            if('GPU_type' not in value.keys()):
                print("*****Error: group %s has not specified GPU type"%key)
                sys.exit()
            gpu_type = value['GPU_type']
            value['GPU'] = comp[gpu_type]

            if('Memory_type' not in value.keys()):
                print("*****Error: group %s has not specified Memory type"%key)
                sys.exit()
            mem_type = value['Memory_type']
            value['MEM'] = comp[mem_type]

            if('Router_type' not in value.keys()):
                print("*****Error: group %s has not specified Router type"%key)
                sys.exit()
            rtr_type = value['Router_type']
            value['ROUTER'] = comp[rtr_type]

        return group

    def get_np(self):
        all_gpu = 0
        group_base, net_base = self.parse()
        for key, value in net_base.items():
            print(key, value)
            if(len(re.findall(r'group\d+_type', key))):
                all_gpu += int(group_base[value]['GPU_count'])
        fw = open('np_tmp', 'w')
        if(all_gpu <= 1 ):
            np = 1
        else:
            np = all_gpu + int(net_base['num'])
        fw.write(str(np))
        os.putenv('np', str(all_gpu + int(net_base['num'])))

class GroupRouteMap:
    def getGroupID(self, nodeId , name):
        return int((nodeId<<8)+self.groupID[name])
    def __init__(self, groups):
        self.groupID = {"CPU": 0, "L1C": 1, "L2C":2, "L3C":3, "GPU": 4, "MEM": 5}

        self.cpus_dst = []
        self.l1cs_dst = []
        self.l2cs_dst = []
        self.gpus_dst = []

        self.cpus_src = []
        self.l1cs_src = []
        self.l2cs_src = []
        self.gpus_src = []
        self.mems_src = []

        for i in range(0, int(groups)):
            CPU_DST = []
            GPU_DST = []
            L1C_DST = []
            L2C_DST = []

            CPU_SRC = []
            GPU_SRC = []
            L1C_SRC = []
            L2C_SRC = []
            MEM_SRC = []

            #cpu dest Gpu,L1C, other Cpus
            CPU_DST.append(self.getGroupID(i,'GPU'))
            #After Ariel has two NICs(Cache and Device), L1C and MEM should be removed from CPU_DST 
            #CPU_DST.append(self.getGroupID(i,'L1C'))
            #CPU_DST.append(self.getGroupID(i,'MEM'))
            for j in range(0, int(groups)):
                if j!= i:
                    CPU_DST.append(self.getGroupID(j,'CPU'))

            #L1C dest MEM, removed by ju
            #L1C_DST.append(self.getGroupID(i,'MEM'))
            L2C_DST.append(self.getGroupID(i,'MEM'))

            #GPU dest CPU
            GPU_DST.append(self.getGroupID(i,'CPU'))

            #CPU src GPU
            CPU_SRC.append(self.getGroupID(i,'GPU'))
            for j in range(0, int(groups)):
                if j!= i:
                    CPU_SRC.append(self.getGroupID(j,'CPU'))
            GPU_SRC.append(self.getGroupID(i,'CPU'))

            MEM_SRC.append(self.getGroupID(i,'L2C'))
            #MEM_SRC.append(self.getGroupID(i,'CPU'))

            self.cpus_dst.append(CPU_DST)
            #self.l1cs_dst.append(L1C_DST)
            self.l2cs_dst.append(L2C_DST)
            self.gpus_dst.append(GPU_DST)
            self.cpus_src.append(CPU_SRC)
            #self.l1cs_src.append(L1C_SRC)
            self.gpus_src.append(GPU_SRC)
            self.mems_src.append(MEM_SRC)

class CPUConfig:
    def __init__(self, cp):
        self.cfg = cp['CPU']
        self.clock = self.cfg['clock']
        #self.applicationParams = dict(cp.items('ariel')) TODO
        self.appParams = cp['core_cfg']
        self.app = self.appParams['application']
        self.mmio_base = int(cp['addr']['mmio_base'], 16)
        self.cpu_nic_base = int(cp['addr']['cpu_nic_base'], 16)
        self.ring_bandwidth= cp['ring_bandwidth']
        self.ring_latency = cp['ring_latency']
        self.coherence_protocol = "MESI"
        self.total_cores = 0
        self.next_id = 0

        if 'ariel' in self.app:
            self.coreConfig = self._arielCoreConfig
        else:
            raise Exception("Unknown application '%s'"% self.app)

    def _arielCoreConfig(self, core_id):
        params = dict({
            #"launcher"            : "%s/intel64/bin/pinbin"%(os.getenv("INTEL_PIN_DIRECTORY", "/dev/null")),
            "maxcorequeue"        : 256,
            "maxtranscore"        : 16,
            "maxissuepercycle"    : self.appParams["max_reqs_cycle"],
            "pipetimeout"         : 0,
            "appargcount"         : 0,
            "arielinterceptcalls" : 1,
            "arielmode"           : 1,
            "corecount"           : self.total_cores,
            # Ariel has a 'memory manager' SubComponent that handles virtual to physical mapping.
            # Tell the default manager how many pages it has
            "memmgr.pagecount0"   : 1048576,
            # The -ifeellucky option is required for Pin 2.14 to work with modern processors. May be removed with newer Pin versions.
            "launchparamcount"    : 1,
            "launchparam0"        : "-ifeellucky",
            })
        params.update(self.appParams)
        return params

    def getL1Params(self):
        return dict({
            "prefetcher": "cassini.StridePrefetcher",
            "prefetcher.reach": 4,
            "prefetcher.detect_range" : 1,
            "cache_frequency": self.clock,
            "cache_line_size": "64",
            "cache_size": "32KB",
            "associativity": 8,
            "access_latency_cycles": 4,
            "replacement_policy": "lru",
            #"coherence_protocol": "MSI",
            "L1": 1,
            # Default params
            "coherence_protocol": self.coherence_protocol,
            # "replacement_policy": "lru",
            # Not neccessary for simple cases:
            #"maxRequestDelay" : "1000000",
            "addr_range_start": 0,
            "addr_range_end": int(self.mmio_base - 1),
            })

    def getL2Params(self):
        return dict({
            "prefetcher": "cassini.StridePrefetcher",
            "prefetcher.reach": 16,
            "prefetcher.detect_range" : 1,
            "cache_frequency": self.clock,
            "cache_size": "256KB",
            "associativity": 8,
            "access_latency_cycles": 6,
            "mshr_num_entries" : 16,
            "memNIC.network_bw": self.ring_bandwidth, # This parameter belongs to the MemNIC SubComponent
            # Default params
            #"cache_line_size": 64,
            "coherence_protocol": self.coherence_protocol,
            "replacement_policy": "lru",
            "addr_range_start": 0,
            "addr_range_end": int(self.mmio_base - 1),
        })
    def getCoreConfig(self, core_id):
        params = dict({
                'clock': self.cfg["clock"],
                'verbose': int(self.appParams["verbose"])
                })
        params.update(self.coreConfig(core_id))
        return params

    def updateTotalCores(self, nCores):
        self.total_cores = nCores

    #build one Ariel CPU per group
    def buildCPU(self, nID):
        cpu = sst.Component("A%d"%(nID), "ariel.ariel")
        cpu.addParams(self.getCoreConfig(0))
        cpu.addParams({"mmio_addr": self.mmio_base,
                        "cpu_nic_base": self.cpu_nic_base,
                    "cpuID": nID})
        self.cpu = cpu
        return cpu
    # build one ariel core of the Ariel CPU of the group
    def buildCoreDevice(self, nID , cID, netCfg, router_map):
        #print("Configuring device interface of core %d..."%cID)
        sst.pushNamePrefix("C%d"%cID)
        # define the device interface of cpu which should be connect the router
        iface = self.cpu.setSubComponent("device", "memHierarchy.standardInterface",cID)
        #iface.addParams(debug_params)
        cpu_nic = iface.setSubComponent("memlink", "memHierarchy.MemNIC")
        cpu_nic.addParams({"group": router_map.getGroupID(nID,'CPU'),
                        "sources": router_map.cpus_src[nID],
                        "destinations": router_map.cpus_dst[nID],
                        "network_bw": self.cfg["network_bw"]})
        sst.popNamePrefix()
        return (cpu_nic , "port" , self.ring_latency)
 
    # build one ariel core of the Ariel CPU of the group
    def buildCoreCache(self, nID , cID, netCfg, router_map):
        #print("Configuring caches of core %d..."%cID)
        sst.pushNamePrefix("C%d"%cID)
        miface = self.cpu.setSubComponent("memory", "memHierarchy.standardInterface",cID)
        #miface.addParams(debug_params)
        l1cache = sst.Component("l1cache", "memHierarchy.Cache")
        l1cache.addParams(self.getL1Params())
        l1_cpulink = l1cache.setSubComponent("cpulink", "memHierarchy.MemLink")
        l1_memlink = l1cache.setSubComponent("memlink", "memHierarchy.MemLink")
        connect("cpu_cache_link",
                miface, "port",
                l1_cpulink, "port",
                self.ring_latency
                ).setNoCut()

        l2 = sst.Component("l2cache", "memHierarchy.Cache")
        l2.addParams(self.getL2Params())
        l2_cpulink = l2.setSubComponent("cpulink", "memHierarchy.MemLink")
        l2_nic = l2.setSubComponent("memlink", "memHierarchy.MemNIC")

        connect("l2_cache_link",
                l1_memlink, "port",
                l2_cpulink, "port",
                self.ring_latency).setNoCut()

        l2_nic.addParams({
                "group": router_map.getGroupID(nID,'L2C'),
                #"sources": l2c_src,
                "destinations" : router_map.l2cs_dst[nID],
                "network_bw": self.ring_bandwidth})

        sst.popNamePrefix()
        return (l2_nic , "port" , self.ring_latency)

    # build one ariel core of the Ariel CPU of the group
    # def buildCore(self, nID , cID, netCfg, router_map):
    #     print("Configuring core %d..."%cID)
    #     ifc = self.cpu.setSubComponent("memory", "memHierarchy.standardInterface", cID)
    #     #ifc.addParams(debug_params)
    #     cpu_nic = ifc.setSubComponent("memlink", "memHierarchy.MemNIC")
    #     cpu_nic.addParams({"group": router_map.getGroupID(nID,'CPU'),
    #                     "destinations": router_map.cpus_dst[nID],
    #                     "network_bw": self.cfg["network_bw"]})
    #     print("cpu_nic %d destinations: %s" % (nID, router_map.cpus_dst[nID]))
    #     #cpu_nic.addParams(debug_params)
    #     return (cpu_nic , "port" , netCfg.cfg["latency"])

class MemConfig:
    def __init__(self, cp):
        self.cfg = cp['MEM']
        #self.mmio_base = int(cp.get('GPU', 'mmio_base'),16)
        self.mmio_base = int(cp['addr']['mmio_base'], 16)
        self.next_id = 0
    def updateTotalMems(self, nMC):
        self.num_MC = nMC

    #nID is groupID, self.next_id is global, and memID is local
    def build(self, nID, memID, netCfg, router_map):
        print("Configuring group %d memory %d..."% (nID, memID))
        sst.pushNamePrefix("group%d"%nID)
        memctrl = sst.Component("memory_%d"%(memID), "memHierarchy.MemController")
        memctrl.addParams(dict({
            "clock" : self.cfg["clock"],
            "memNIC.network_bw": self.cfg["network_bw"],   # This parameter belongs to the MemNIC SubComponent
            "backing" : "none",
            #"interleave_size": "%dB"%int(self.cfg["interleave_size"]),
            #"interleave_step": "%dB"%(self.num_MC * int(self.cfg["interleave_size"])),
            "addr_range_start" : memID * int(self.cfg["interleave_size"]),
            "addr_range_end" : (self.mmio_base-1),
            }))
        memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
        memory.addParams(dict({
            "access_time" : "30ns",
            "mem_size" : "%d%s"%(int(''.join(filter(str.isdigit, self.cfg["capacity"]))) // (self.num_MC), (''.join(filter(str.isalpha, self.cfg["capacity"])))),
            }))

        dc = sst.Component("dc_%d"%(memID), "memHierarchy.DirectoryController")
        dc.addParams(dict({
            #"interleave_size": "%dB"%int(self.cfg["interleave_size"]),
            #"interleave_step": "%dB"%(self.num_MC * int(self.cfg["interleave_size"])),
            "entry_cache_size": 256*1024*1024, #Entry cache size of mem/blocksize
            "clock": self.cfg["clock"],
            "memNIC.network_bw": self.cfg["network_bw"],   # This parameter belongs to the MemNIC SubComponent
            "memNIC.group": router_map.getGroupID(nID,'MEM'),
            "memNIC.sources": router_map.mems_src[nID],
            "addr_range_start" : memID * int(self.cfg["interleave_size"]),
            #"addr_range_end" : (int(''.join(filter(str.isdigit, self.cfg["capacity"]))) * 1024 * 1024) - (self.num_MC * int(self.cfg["interleave_size"])) + (self.next_id * int(self.cfg["interleave_size"])),
            "addr_range_end" : (self.mmio_base-1),
            }))
        print("memNIC.group: %s" % (router_map.getGroupID(nID,'MEM')))

        connect("mem_link_%d"%memID,
                memctrl, "direct_link",
                dc, "memory",
                netCfg.cfg["latency"]).setNoCut()

        self.next_id += 1

        sst.popNamePrefix()
        return (dc, "network", netCfg.cfg["latency"])


class GPUConfig:
    def __init__(self, cp, gpuFile):
        self.gpuFile = gpuFile
        self.cfg = cp['GPU']
        #self.mmio_base = int(cp.get('GPU', 'mmio_base'),16)
        self.mmio_base = int(cp['addr']['mmio_base'], 16)
        self.next_id = 0
        self.gpuFile = gpuFile

    #nID is groupID, self.next_id is global, and memID is local
    def build(self, nID, gpuID, netCfg, router_map):
        print("Configuring group %d gpu %d..."% (nID, gpuID))
        sst.pushNamePrefix("group%d"%nID)
        mmio = sst.Component("gpu%d" % gpuID, "gpgpuSimInterface.simpleGpuInterface")
        mmio_addr = self.mmio_base + gpuID * 1024
        mmio.addParams({
            "clock" : self.cfg["clock"],
            "out_file_path" : self.gpuFile,
            "group_id": nID,
            "gpu_id": gpuID,
            "addr_range_begin": mmio_addr,
            "addr_range_end": mmio_addr + 1,  #TODO ?
        })
        mmio_iface = mmio.setSubComponent("memory", "memHierarchy.standardInterface")
        mmio_nic = mmio_iface.setSubComponent("memlink", "memHierarchy.MemNIC")
        mmio_nic.addParams({"group": router_map.getGroupID(nID,'GPU'),
                            "sources" : router_map.gpus_src[nID],
                            "destinations" : router_map.gpus_dst[nID],
                            "network_bw": self.cfg['network_bw']})
        sst.popNamePrefix()
        return (mmio_nic, "port", netCfg.cfg["latency"])

#Inheritance class of merlin.topoMesh (by qiusk)
#class topoMesh_custom(merlin.topoMesh):
#    def __init__(self):
#        merlin.topoMesh.__init__(self)
#        self.eps = []
#    def setEndPoint(self, endPoint):
#        self.eps.append(endPoint)
#        def epFunc(epID):
#            return self.eps[epID]
#        self._getEndPoint = epFunc




class GroupConfig:
    def __init__(self, cp, chipNet, gpuFile):
        #self.cfg = dict(cp.items('Groups'))
        self.cfg = cp
        self.chipNet = chipNet
        #self.comCfg = self.cfg['Config']
        self.comCfg = self.cfg
        self.cpuConfig = CPUConfig(self.cfg)
        self.memConfig = MemConfig(self.cfg)
        self.gpuConfig = GPUConfig(self.cfg, gpuFile)
        #self.netCfg = NetConfig(self.cfg, cfgGroup='', cfgPrefix='ring_') #TODO
        #self.router_map = GroupRouteMap(self.comCfg["group_count"])
        self.num_gpus = int(self.cfg['GPU_count'])
        self.num_mcs = int(self.cfg['Memory_count'])
        self.cores_per_group = int(self.cfg['CPU']['core_num'])
        #use default ring net parameters
        self.ring_bandwidth= "96GB/s"
        self.ring_latency = "300ps"
        self.coherence_protocol = "MESI"
        self.mmio_base = int(cp['addr']['mmio_base'], 16)
        self.l3cache_blocks =int(self.cfg['l3cache_blocks'])
        self.l3cache_block_size = self.cfg['l3cache_block_size']
        self.next_l3_id = 0
        self.ringRouterMap = {}
        #L3cache blocks need to add, and extra add a netBridge rtr
        self.num_ring_stops = int(self.cores_per_group) + self.l3cache_blocks + self.num_mcs + self.num_gpus + 1

    def setNumGroups(self, numGroups):
        self.num_groups = numGroups
        #self.cpuConfig.updateTotalCores(numGroups * int(self.cfg['cores']))
        #self.memConfig.updateTotalMems(numGroups * int(self.cfg['memory_controllers']))
        #by ju, the cores and memory controller of every group are private
        self.cpuConfig.updateTotalCores(1 * int(self.cfg['CPU']['core_num']))
        self.memConfig.updateTotalMems( 1* int(self.num_mcs))
        #self.router_map = GroupRouteMap(self.num_groups)

    def setRouterMap(self, router_map):
        self.router_map = router_map

    def getNumCores(self):
        return int(self.cfg['CPU']['core_num'])

    def getL3Params(self):
        return dict({
            "access_latency_cycles" : "12",
            "cache_frequency" : self.clock,
            "associativity" : "16",
            "cache_size" : self.cfg["l3cache_block_size"],
            "mshr_num_entries" : "4096",
            "memNIC.network_bw": self.ring_bandwidth,   # This parameter belongs to the MemNIC SubComponent
            # Distributed caches
            #"num_cache_slices" : self.groups * self.l3cache_blocks_per_group,
            "num_cache_slices" : self.l3cache_blocks,
            "slice_allocation_policy" : "rr",
            # Default params
            # "replacement_policy" : "lru",
            # "cache_line_size" : "64",
            "coherence_protocol" : self.coherence_protocol,
            "addr_range_start": 0,
            "addr_range_end": self.mmio_base - 1,
            })
    def buildL3(self, sID, netCfg):
        print("Building L3cache_%d"%sID)
        l3cache = sst.Component("l3cache_%d"%(sID), "memHierarchy.Cache")
        l3cache.addParams(self.getL3Params())
        l3cache.addParams({
            "slice_id" : sID 
            })
        return (l3cache, "directory", netCfg.cfg["latency"])

    # 2.66 GHz clock, moves 64B / cycle, plus overhead -> 36B/c
    #bandwidth: 96GB/s, flit_size: 8B
    # Ring router ports 0,1 are used for topology,ports 2,3 are used for local traffic
    def getRingParams(self):
        return dict({
            "output_latency" : "25ps",
            "xbar_bw" : "96GB/s",
            "input_buf_size" : "2KB",
            "input_latency" : "25ps",
            "num_ports" : "4",
            "flit_size" : "8B",
            "output_buf_size" : "2KB",
            "link_bw" : "96GB/s",
        })

    def getRingTopoParams(self):
        return dict({
            "shape" : self.num_ring_stops,
            "local_ports" : "2",
            "width" : "1",
        })

    def buildGroup(self, groupID, bridge):
        print("Building group", groupID)
        # Build CPU Ring NoC
        print("Configuring CPU Ring Network-on-Chip... Total ring stops: %d"%self.num_ring_stops)
        for next_ring_stop in range(self.num_ring_stops):
            ring_rtr = sst.Component("rtr_%d"%next_ring_stop, "merlin.hr_router")
            ring_rtr.addParams(self.getRingParams())
            ring_rtr.addParam( "id", next_ring_stop)
            topo = ring_rtr.setSubComponent("topology", "merlin.torus")
            topo.addParams(self.getRingTopoParams())
            self.ringRouterMap["rtr.%d"%next_ring_stop] = ring_rtr

        print("Configuring CPU Ring Network-on-Chip Links...")
        for next_ring_stop in range(self.num_ring_stops):
            if next_ring_stop == self.num_ring_stops - 1:
                rtr_link = sst.Link("rtr_" + str(next_ring_stop))
                rtr_link.connect( (self.ringRouterMap["rtr." + str(next_ring_stop)], "port0", self.netCfg.cfg["latency"]), (self.ringRouterMap["rtr.0"], "port1", self.netCfg.cfg["latency"] ) )
            else:
                rtr_link = sst.Link("rtr_" + str(next_ring_stop))
                rtr_link.connect( (self.ringRouterMap["rtr." + str(next_ring_stop)], "port0", self.netCfg.cfg["latency"]), (self.ringRouterMap["rtr." + str(next_ring_stop+1)], "port1", self.netCfg.cfg["latency"]) )

        next_network_id = 0
        # Build CPU, one Ariel CPU of per group
        # Connect cpu's device nic to the router's local port 2
        print("Build CPU...")
        self.cpuConfig.buildCPU(groupID)

        # Connect Cores' caches and devices
        for next_core_id in range(self.cores_per_group):
            # Connect the cores of the CPU
            print("Configuring CPU core%d cache link on ring stop%d."%(next_core_id,next_network_id))
            link = sst.Link("l2c_nic_link%d"%next_core_id)
            link.connect( (self.ringRouterMap["rtr.%d"%next_network_id], "port2", self.netCfg.cfg["latency"]),
                    self.cpuConfig.buildCoreCache(groupID, next_core_id, self.netCfg, self.router_map))
            link.setNoCut()

            print("Configuring CPU core%d device link on ring stop%d."%(next_core_id,next_network_id))
            link = sst.Link("cpu_dev_link%d"%next_core_id)
            link.connect( (self.ringRouterMap["rtr.%d"%next_network_id], "port3", self.netCfg.cfg["latency"]) ,
                        self.cpuConfig.buildCoreDevice(groupID, next_core_id, self.netCfg, self.router_map))
            link.setNoCut()
            next_network_id = next_network_id + 1

        # Connect L3s
        for l3ID in range(self.l3cache_blocks):
            print("Configuring L3cache slice%d link on ring stop%d."%(l3ID,next_network_id))
            sst.Link("L3_%d"%l3ID).connect(
                   (self.ringRouterMap["rtr.%d"%next_network_id], "port2", self.netCfg.cfg["latency"]),
                   self.buildL3(l3ID, self.netCfg))
            next_network_id = next_network_id + 1

        # Connect Memories
        for memID in range(self.num_mcs):
            print("Configuring mem%d link on ring stop%d."%(memID,next_network_id))
            link = sst.Link("MEM_%d"%memID)
            link.connect( (self.ringRouterMap["rtr.%d"%next_network_id], "port2", self.netCfg.cfg["latency"]),
                    self.memConfig.build(groupID, memID, self.netCfg, self.router_map))
            link.setNoCut()
            next_network_id = next_network_id + 1

        #global GPU ID = cpuID<<8 + local gpuID, cpuID==GroupID
        for gpuID in range(int(self.cfg["gpus"])):
            print("Configuring GPU%d link on ring stop%d."%(gpuID,next_network_id))
            link = sst.Link("GPU_%d"%gpuID)
            link.connect( (self.ringRouterMap["rtr.%d"%next_network_id], "port2", self.netCfg.cfg["latency"]),
                        self.gpuConfig.build(groupID, gpuID, self.netCfg, self.router_map))
            next_network_id = next_network_id + 1

        # Connect the ring router to the net bridge which connects to the MESH 
        print("Configuring bridgeRtr link on ring stop%d."%(next_network_id))
        link = sst.Link("bridgeRtr")
        # inner port connect to Ring : use self.netCfg.cfg["latency"]
        link.connect( (self.ringRouterMap["rtr.%d"%next_network_id], "port2", self.netCfg.cfg["latency"]),
                    (bridge, "network0", self.netCfg.cfg["latency"]))
        link.setNoCut()

    def build(self, nID):
        sst.pushNamePrefix("group%d"%nID)
        bridge = sst.Component("bridge", "merlin.Bridge")
        #print(self.netCfg.cfg)
        #Chip Net is Mesh, use chipNet[]
        bridge.addParams({
            "translator": "memHierarchy.MemNetBridge",
            "debug": 0,
            "debug_level" : 0,
            "network_bw" : self.chipNet["bandwidth"],
            })
        self.buildGroup(nID, bridge)
        # outer port connect to Mesh : use self.chipNet["latency"] 
        ret = (bridge, "network1", self.chipNet["latency"])
        sst.popNamePrefix()
        return ret



class ChipConfig:

    def __init__(self, jsonFile, cfgFile, gpuFile, **kwargs):
        comcfg = ConfigParser.ConfigParser()
        if not comcfg.read(cfgFile):
            raise Exception('Unable to read file "%s"'%cfgFile)

        jp = JsonParser(jsonFile)

        group_base, net_base = jp.parse()
        self.net = net_base
        self.gpuFile = gpuFile

        self.verbose = "verbose" in kwargs and kwargs["verbose"]

        # get all groups(endpoints), key = XY (by qiusk)
        self.groups = self.group_in_topo(group_base, net_base)
        #self.groupConfig = GroupConfig(groupCfg)

        #each group add common configuration
        for key in group_base.keys():
            for sec in comcfg.sections():
                group_base[key][sec] = dict(comcfg.items(sec))

        #self.groupConfig = GroupConfig(group_base['GROUP_A'], net_base)
        #self.netConfig = NetConfig(net_base)
        #self.groupConfig.setNumGroups(self.netConfig.endPointCount())



    #get each group configuration in mesh
    def group_in_topo(self, group_base, net_base):
        groups = {}
        for key, value in net_base.items():
            if(len(re.findall(r'group\d+_type', key))):
                name = key.split('_')[0]
                groupCfg = group_base[value]
            elif(len(re.findall(r'group\d+_xy', key))):
                groupXY = [int(i) for i in value if i not in [" ",","]]
                groups[''.join(str(j) for j in groupXY)] = groupCfg
            else:
                pass
        #groups: key = XY, value = configuration
        return groups


if __name__ == "__main__":
    config = ChipConfig("design.json", "selfcfg.cfg", "../gpgpusim.config", verbose=1)
    jp = JsonParser("design.json")
    group_base, net_base = jp.parse()
    #print(group_base)
    # get all groups(endpoints), key = XY (by qiusk)
    groups = config.group_in_topo(group_base, net_base)
    #print(groups)

